; creates bits stream and returns pointer
proc BitsCreateNew .buf_size
        push    ebx edi
        invoke  GetProcessHeap
        mov     edi,eax
        invoke  HeapAlloc,eax,HEAP_ZERO_MEMORY,sizeof.BitStream
        or      eax,eax
        jz      .finish
        mov     ebx,eax
        invoke  HeapAlloc,edi,HEAP_ZERO_MEMORY,[.buf_size]
        or      eax,eax
        jz      .finish
        mov     [ebx+BitStream.buf],eax
        mov     [ebx+BitStream.ptr],eax
        mov     [ebx+BitStream.bit_ptr],0
        mov     eax,ebx
        pop     edi ebx
     .finish:
        return
endp

; create bitstream over existing buffer
proc BitsCreateFrom .bitbuf
        push    ebx edi
        invoke  GetProcessHeap
        mov     edi,eax
        invoke  HeapAlloc,eax,HEAP_ZERO_MEMORY,sizeof.BitStream
        or      eax,eax
        jz      .finish
        mov     ebx,eax
        mov     eax,[.bitbuf]
        mov     [ebx+BitStream.buf],eax
        mov     [ebx+BitStream.ptr],eax
        mov     [ebx+BitStream.bit_ptr],0
        mov     eax,ebx
        pop     edi ebx
     .finish:
        ret
endp

; destroys given bitstream, but not the buffer (!)
proc BitsDestroy .stream
       push     eax
       invoke   GetProcessHeap
       invoke   HeapFree,eax,0,[.stream]
       pop      eax
       ret
endp

; destroys given bitstream with buffer
proc BitsDestroyBuf .stream
       push     eax ebx
       invoke   GetProcessHeap
       mov      ebx,eax
       mov      eax,[.stream]
       mov      eax,[eax+BitStream.buf]
       invoke   HeapFree,ebx,0,eax
       invoke   HeapFree,ebx,0,[.stream]
       pop      ebx eax
       return
endp


; sets given stream to position 0
proc BitsReset .stream
        push    eax ebx
        mov     ebx,[.stream]
        mov     [ebx+BitStream.bit_ptr],0
        mov     eax,[ebx+BitStream.buf]
        mov     [ebx+BitStream.ptr],eax
        pop     ebx eax
        return
endp

; puts given number of bits into stream
proc BitsPut .stream,.bits,.bit_count
        push    eax ebx ecx edx esi edi
        ; generate mask in edi:esi
        xor     eax,eax
        dec     eax
        mov     esi,eax
        mov     edi,eax
        mov     ebx,[.stream]
        mov     ecx,[.bit_count]
        shl     esi,cl
        mov     ecx,[ebx+BitStream.bit_ptr]
        shld    edi,esi,cl
        shld    esi,eax,cl
        ; load new bits into edx:ecx; at this point ecx==[ebx+BitStream.bit_ptr]
        xor     edx,edx
        mov     eax,[.bits]
        shld    edx,eax,cl
        shl     eax,cl
        mov     ecx,eax
        ; finally, store new bits using
        mov     ebx,[ebx+BitStream.ptr]
        and     [ebx],esi
        or      [ebx],ecx
        add     ebx,4
        and     [ebx],edi
        or      [ebx],edx
        ; and fix the pointers
        mov     ebx,[.stream]
        mov     eax,[.bit_count]
        add     [ebx+BitStream.bit_ptr],eax
        cmp     [ebx+BitStream.bit_ptr],32
        jb      .finish
        sub     [ebx+BitStream.bit_ptr],32
        add     [ebx+BitStream.ptr],4
      .finish:
        pop     edi esi edx ecx ebx eax
        ret
endp

; reads given number of bits form the stream
proc BitsGet .stream,.bit_count
        push    ebx ecx edx esi
        ; generate mask (in esi)
        xor     esi,esi
        dec     esi
        mov     ecx,[.bit_count]
        shl     esi,cl
        not     esi
        mov     ebx,[.stream]
        mov     eax,[ebx+BitStream.ptr]
        mov     edx,[eax+4]
        mov     eax,[eax]
        mov     ecx,[ebx+BitStream.bit_ptr]
        shrd    eax,edx,cl
        and     eax,esi
        ; fix the pointers
        mov     ebx,[.stream]
        mov     edx,[.bit_count]
        add     [ebx+BitStream.bit_ptr],edx
        cmp     [ebx+BitStream.bit_ptr],32
        jb      .finish
        sub     [ebx+BitStream.bit_ptr],32
        add     [ebx+BitStream.ptr],4
     .finish:
        pop     esi edx ecx ebx
        ret
endp

; returns size (in bytes) of given stream
proc BitsGetSize .stream
        push    ebx edx
        mov     ebx,[.stream]
        xor     edx,edx                         ; get number of bytes in additional bits
        mov     eax,[ebx+BitStream.bit_ptr]
        or      eax,eax
        jz      .no_extra_bits
        shr     eax,3
        inc     eax
        mov     edx,eax
     .no_extra_bits:
        mov     eax,[ebx+BitStream.ptr]
        sub     eax,[ebx+BitStream.buf]
        add     eax,edx
        pop     edx ebx
        ret
endp